⚡ 30 秒速记
- 核心判断:响应式系统由依赖收集、变更触发和调度刷新组成,关键不在“监听数据”,而在精确关联状态与副作用
- 原理主线:围绕 「Vue 初始化」、「模板渲染」、「组件渲染」 建立输入、状态变化与输出之间的因果关系
- 文章范围:通过图文并茂的方式,详细讲解了Vue响应式原理的实现机制,包括数据劫持、依赖收集、数据监听等核心流程,帮助读者深入理解Vue如何实现数据与视图的自动同步更新。
- 边界与代价:
Vue 2的defineProperty与Vue 3的Proxy在新增属性、数组和集合类型上边界不同 - 工程落地:排查视图不更新或重复更新时,应沿读取是否被追踪、写入是否触发、任务是否被去重三段定位
Vue 响应式的关键,是在渲染读取数据时建立 Data 与 Watcher 的依赖关系,并在数据修改后只通知相关视图更新。 Vue 2 初始化时通过 Object.defineProperty 为属性绑定 getter/setter,渲染 Watcher 执行 _render 时触发读取,由 Dep 完成依赖收集。写入数据会调用 Dep.notify,Watcher.update 再进入异步队列,重新生成 VNode 并通过 patch 更新真实 DOM。这套描述针对 Vue 2;理解 Vue 3 时应切换到 Proxy、effect 和新的渲染器模型。
这篇文章不要按 API 清单来背。先用上面的 Mind Map 建立全局结构,再通过交互 DEMO 观察正常路径和边界路径如何改变状态;阅读正文时重点核对每一步的输入、负责执行的参与者、产生的中间状态以及最终可观察结果。遇到版本敏感结论,要把“历史实现”“当前行为”和“工程兼容策略”分开说明;遇到性能或架构取舍,则用实际指标、失败现象和验证手段支撑判断。
版本校准: 本文出现 Object.defineProperty、Dep、Watcher 和双端 Diff 时,主要描述 Vue 2 实现;Vue 3 使用 Proxy、effect 与新的渲染器路径。Vue 2 已于 2023-12-31 结束维护,新项目应以 Vue 3 为基线,旧项目参考 Vue 2 EOL 官方说明 制定迁移与安全策略。
# Vue 初始化
先从最简单的一段 Vue 代码开始:
<template>
<div>
{{ message }}
</div>
</template>
<script>
new Vue({
data() {
return {
message: "hello world",
};
},
});
</script>
- 这段代码很简单,最终会在页面上打印一个 hello world,它是如何实现的呢?
- 我们从源头:new Vue 的地方开始分析。
// 执行 new Vue 时会依次执行以下方法
// 1. Vue.prototype._init(option)
// 2. initState(vm)
// 3. observe(vm._data)
// 4. new Observer(data)
// 5. 调用 walk 方法,遍历 data 中的每一个属性,监听数据的变化。
function walk(obj) {
const keys = Object.keys(obj);
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]);
}
}
// 6. 执行 defineProperty 监听数据读取和设置。
function defineReactive(obj, key, val) {
// 为每个属性创建 Dep(依赖搜集的容器,后文会讲)
const dep = new Dep();
// 绑定 get、set
Object.defineProperty(obj, key, {
get() {
const value = val;
// 如果有 target 标识,则进行依赖搜集
if (Dep.target) {
dep.depend();
}
return value;
},
set(newVal) {
val = newVal;
// 修改数据时,通知页面重新渲染
dep.notify();
},
});
}